﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace PhysicsSimulator
{
    public enum SpringState
    {
        IDLE,
        WAIT,
        DRAG,
        USING,
    }

    class CSprings : CGameObject
    {
        private const float RATIO = 0.375f;
        private const float GRAVITY = 9.8f;

        Vector2 _position = Vector2.Zero;

        CSprite _spring_head = null;
        CSprite _spring_end = null;
        CSprite _spring_long_body = null;
        CSprite _spring_short_body = null;

        CSprite _l0_sprite = null;

        SpringState _currentState = SpringState.IDLE;
        public SpringState CurrentState
        {
            get
            {
                return _currentState;
            }
            set
            {
                _currentState = value;
            }
        }
        //CSprite _weight_sprite = null;
        //public CSprite WeightSprite
        //{
        //    get 
        //    {
        //        return _weight_sprite;
        //    }
        //    set
        //    {
        //        _weight_sprite = value;
        //    }
        //}

        CWeightObject _weight_object = null;
        public CWeightObject WeightObject
        {
            get
            {
                return _weight_object;
            }
            set
            {
                _weight_object = value;
            }
        }

        float m_fSpringLenght = 0.0f;

        int m_iSpringCount = 0;

        List<CSprite> _springs_units = new List<CSprite>();
        double m_dTime = 0;

        CVectorForce P, F;

        float m_fLo = 0.0f;
        public float L0
        {
            get
            {
                return m_fLo;
            }
        }
        public void SetLo(float value)
        {
            m_fLo = value;
            _l0_sprite.setPositionY(_spring_head.Height + m_fLo*0.97f);
            _l0_sprite.setPositionX(240.0f);
        }

        float m_fLcb=0.0f;
        public float LCB
        {
            get
            {
                return m_fLcb;
            }
        }

        float m_fLmin=0.0f;
        public float Lmin
        {
            get
            {
                return m_fLmin;
            }
        }

        float m_fLmax=0.0f;
        public float Lmax
        {
            get
            {
                return m_fLmax;
            }
        }
        float m_fK = 5.0f;
        float m_fA=0.0f;
        public float A
        {
            get
            {
                return m_fA;
            }
        }

        float m_fOmega=0.0f;
        float m_fPhi;

        public float Wt =0.0f;
        public float Wd = 0.0f;
        public float W = 0.0f;

        float m_fD_L0 = 0.0f;

        public CSprings(SpriteBatch spriteBatch)
            : base(spriteBatch)
        {

        }

        public override void Init(Microsoft.Xna.Framework.Content.ContentManager contentManager)
        {
            _spring_head = new CSprite(contentManager.Load<Texture2D>("Images/Springs/spring_head"));
            _spring_head.Anchor = new Vector2(0.5f, 0);

            _spring_end = new CSprite(contentManager.Load<Texture2D>("Images/Springs/spring_end"));
            _spring_end.Anchor = new Vector2(0.5f, 0.0f);

            _spring_short_body = new CSprite(contentManager.Load<Texture2D>("Images/Springs/spring_short_body"));
            _spring_long_body = new CSprite(contentManager.Load<Texture2D>("Images/Springs/spring_long_body"));

            _l0_sprite = new CSprite(contentManager.Load<Texture2D>("Images/Springs/l0"));

            P = new CVectorForce(SpriteBatch);
            P.Init(contentManager);
            P.Create(Vector2.Zero, new Vector2(0, -100), 3, Color.Pink);

            F = new CVectorForce(SpriteBatch);
            F.Init(contentManager);
            F.Create(Vector2.Zero, new Vector2(0, 100), 3, Color.Green);

            UpdateSprings();
            SetLo(200.0f);

            base.Init(contentManager);
        }

        public override void Update(GameTimerEventArgs timer)
        {            
            m_dTime += timer.ElapsedTime.TotalSeconds*35;//speed ratio
            //m_fSpringLenght = (float)(200 +  150*Math.Cos(m_dTime*5f));
            UpdateSprings();
            //if (_weight_sprite != null)
            //{
            //    P.Update(timer);
            //    F.Update(timer);
            //}
            W = 0.5f * m_fK * (m_fA/100.0f) * (m_fA/100.0f);

            float ot = (float)(m_fOmega * m_dTime + m_fPhi);

            Wt = (float)(W * Math.Sin(ot) * Math.Sin(ot));
            Wd = (float)(W * Math.Cos(ot) * Math.Cos(ot));

            switch (_currentState)
            {
                case SpringState.IDLE:
                    m_fSpringLenght = m_fLo;
                    break;
                case SpringState.DRAG:
                    break;
                case SpringState.USING:
                    if (m_fA > 0)
                    {
                        m_fSpringLenght = (float)(m_fLcb + m_fA * Math.Cos(m_dTime * m_fOmega + m_fPhi));
                        if (m_fA > 0)
                        {
                            m_fA -= Settings.SpringsEnviromentRatio / 35.0f;
                            m_fLmax -= Settings.SpringsEnviromentRatio / 35.0f;
                            m_fLmin += Settings.SpringsEnviromentRatio / 35.0f;
                            if (m_fA < 0)
                                m_fA = 0;
                        }
                    }

                    

                    break;
            }
            base.Update(timer);
        }

        public override void Draw(GameTimerEventArgs timer)
        {
            SpriteBatch.Draw(_l0_sprite.Texture, _l0_sprite.Position, null, Color.White, 0, _l0_sprite.Origin, 1, SpriteEffects.None, 0);
            SpriteBatch.Draw(_spring_head.Texture,_position,null,Color.White,0,_spring_head.Origin,1,SpriteEffects.None,0);
            foreach (CSprite sprite in _springs_units)
            {
                SpriteBatch.Draw(sprite.Texture,sprite.Position,null,Color.White,sprite.Rotation,sprite.Origin,1.0f,SpriteEffects.None,0);
            }
            
            SpriteBatch.Draw(_spring_end.Texture, _spring_end.Position,null,Color.White,_spring_end.Rotation,_spring_end.Origin,1,SpriteEffects.None,0);

            //if (_weight_sprite != null)
            //{                
            //    SpriteBatch.Draw(_weight_sprite.Texture, _weight_sprite.Position, null, Color.White, _weight_sprite.Rotation, _weight_sprite.Origin, 1.0f, SpriteEffects.None, 0);
            //    SpriteBatch.End();
            //    P.Draw(timer);
            //    F.Draw(timer);
            //    SpriteBatch.Begin();
            //}
            base.Draw(timer);
        }

        public void Create(Vector2 position,float lenght, int count)
        {
            _position = position;
            m_fSpringLenght = lenght;

            _spring_end.Position = position + new Vector2(0, lenght + _spring_head.Height);
            m_iSpringCount = count;
            UpdateSprings();
        }

        public float getSpringsHeadHeight()
        {
            return _spring_head.Height;
        }

        public Vector2 getWeightPosition()
        {
            if (_currentState != SpringState.IDLE)
                return new Vector2(_weight_object.Position.X, _weight_object.Position.Y);
            return new Vector2(-480, -800);
        }

        void UpdateSprings()
        {
            _springs_units.Clear();
            float spring_unit_lenght = m_fSpringLenght / m_iSpringCount;

            float alpha = (float)Math.Asin(m_fSpringLenght/(m_iSpringCount*_spring_long_body.Width));

            Vector2 unit_position = new Vector2(_position.X,_position.Y + _spring_head.Height);

            CSprite s0 = new CSprite(_spring_short_body.Texture);
            s0.Position = unit_position + new Vector2(0,-_spring_head.Width/2);
            s0.Rotation = alpha;
            s0.Anchor = new Vector2(0.0f, 0.5f);
            _springs_units.Add(s0);
            unit_position.Y += m_fSpringLenght / m_iSpringCount;
            alpha = (float)(Math.PI - alpha);
            for (int i = 0; i < m_iSpringCount-2; i++)
            {
                CSprite si = new CSprite(_spring_long_body.Texture);
                si.Rotation = alpha;
                si.Position = unit_position;
                _springs_units.Add(si);
                unit_position.Y += m_fSpringLenght / m_iSpringCount;
                alpha = (float)(Math.PI - alpha);
            }
            CSprite sn = new CSprite(_spring_short_body.Texture);
            sn.Position = unit_position + new Vector2(0,_spring_head.Width/2);
            sn.Rotation = alpha;
            sn.Anchor = new Vector2(1.0f,0.5f);
            _springs_units.Add(sn);

            _spring_end.Position = unit_position;           

            //if (_weight_sprite != null)
            //{
            //    _weight_sprite.Position = unit_position + new Vector2(0,_spring_end.Height + _weight_sprite.Height/2-4);
            //    P.Position = _weight_sprite.Position;
            //    F.Position = _weight_sprite.Position;
            //    F.VectorForce = new Vector2(0,-m_fSpringLenght/2.5f);
            //}

            if (_weight_object != null)
            {
                _weight_object.Position = unit_position + new Vector2(0, _spring_end.Height + _weight_object.Sprite.Height / 2 - 4);
            }

            
        }

        public void AddWeightObject(CWeightObject wo)
        {
            _weight_object = wo;
            wo.CurrentState = WeightObjectState.HOLD;

            m_fD_L0 = (wo.Weight*GRAVITY)*100/(m_fK);
            m_fLcb = m_fLo + m_fD_L0 / RATIO;
            m_fSpringLenght = m_fLcb;
            _currentState = SpringState.WAIT;
        }

        public void Reset()
        {
            m_fSpringLenght = m_fLo;
            m_fA = 0.0f;
            if (_weight_object != null)
            {
                _weight_object.Reset();
            }
            _currentState = SpringState.IDLE;
        }

        public void UpdateSpringLenght(float dy)
        {
            m_fSpringLenght += dy;
        }

        public void SetInUseState()
        {
            m_dTime = 0;
            _currentState = SpringState.USING;
            _weight_object.CurrentState = WeightObjectState.USING;
            if (m_fSpringLenght >= m_fLcb)
            {
                m_fLmax = m_fSpringLenght;
                m_fLmin = 2 * m_fLcb - m_fLmax;
                m_fPhi = 0;
            }
            else
            {
                m_fLmin = m_fSpringLenght;
                m_fLmax = 2 * m_fLcb - m_fLmin;
                m_fPhi = (float)Math.PI;
            }

            m_fA = (m_fLmax - m_fLmin) / 2;            

            m_fOmega = (float)(Math.Sqrt(_weight_object.Weight/m_fK));
        }
    }
}
